/**
 * Options.java
 *
 * @author BJ Premore
 */


package SSF.OS.OSPF;


import java.io.*;
import java.util.*;
import com.renesys.raceway.DML.*;
import SSF.Net.*;
import SSF.Net.Util.*;
import SSF.OS.*;
import SSF.OS.BGP4.Util.StringManip;


// ======================= class SSF.OS.OSPF.Options ======================= //
/**
 * This class helps OSPF manage run-time options.
 */
public class Options {

  // ......................... constants ........................... //

  /** Whether or not plain debugging messages are turned on.  Error and warning
   *  messages are always turned on.  In order to run an efficient simulation,
   *  this code should be changed manually to set <code>debugging_on</code> to
   *  false and then recompiled. */
  public static final boolean debugging_on = true;

  /** The number of boolean options kept in the opt array. */
  private static final int num_bool_opts = 8;

  /** The names of several boolean debugging attributes/options. */
  private static final String[] bool_opt_names =
  {
    "show_interfaces",     "show_as_num",         "show_as_area",
    "show_router_type",    "show_hello_packets",  "show_lsas",
    "show_lsdb",           "show_fwd_table"
  };

  // . . . . . . . . . . debugging option constants . . . . . . . . . .
  /** Indicates option to print IP and OSPF interfaces on which the router is
   *  running. */
  public static final int IFS           =  0;
  /** Indicates option to print the number of the AS in which the OSPF router
   *  is located. */
  public static final int AS_NUM        =  1;
  /** Indicates option to print the AS(es) and area(s) number in which OSPF
   *  router is located.  (Area numbers are not yet available.) */
  public static final int AS_AREA       =  2;
  /** Indicates option to print router's classification. */
  public static final int RTR_TYPE      =  3;
  /** Indicates option to show when hello packets are sent and received.
   *  (Currently not implemented.) */
  public static final int HELLO         =  4;
  /** Indicates option to show when LSAs have been sent. */
  public static final int LSA           =  5;
  /** Indicates option to show info about the link state database. */
  public static final int LSDB          =  6;
  /** Indicates option to show info about the local forwarding table. */
  public static final int FWD_TABLE     =  7;

  // . . . . . . . . . . validation test constants . . . . . . . . . .
  /** Indicates that no validation tests are being performed. */
  public static final int NO_TEST       = -1;
  /** Indicates that the `routertype1' validation test is being performed. */
  public static final int ROUTERTYPE1   =  1;
  /** Indicates that the `interfaces1' validation test is being performed. */
  public static final int INTERFACES1   =  2;
  /** Indicates that the `lsdb1' validation test is being performed. */
  public static final int LSDB1         =  3;
  /** Indicates that the `fwdtable1' validation test is being performed. */
  public static final int FWDTABLE1     =  4;
  /** Indicates that the `routertype2' validation test is being performed. */
  public static final int ROUTERTYPE2   =  5;
  /** Indicates that the `interfaces2' validation test is being performed. */
  public static final int INTERFACES2   =  6;
  /** Indicates that the `lsdb2' validation test is being performed. */
  public static final int LSDB2         =  7;
  /** Indicates that the `fwdtable2' validation test is being performed. */
  public static final int FWDTABLE2     =  8;

  // ........................ member data .......................... //

  /** Whether or not to use full NHI addressing.  Using it means that AS
   *  numbers, IP prefixes and IP addresses will be converted to NHI addresses
   *  in output whenever possible. */
  public static boolean usenhi = false;

  /** An array for several boolean debugging attributes/options. */
  public static boolean[] opt;

  /** Whether or not any wrap-up functions need to be performed at the end of
   *  the simulation.  It is true only if at least one of the dump options has
   *  been set. */
  public static boolean wrapup = false;

  /** Indicates whether or not this simulation is a validation test (-1 means
   *  it isn't, non-negative means it is) and if so, which test number it
   *  is. */
  public static int validation_test = NO_TEST;

  /** Used with certain validation tests.  It may take on different meanings
   *  from one to the next. */
  private static int numdata = 0;


  // ---------------------------- Options.config --------------------------- //
  /**
   * Configures options set with the <code>ospfoptions</code> attribute in DML.
   * All such options are "global" OSPF options--that is, they apply to all
   * instances of OSPF in the simulation.
   *
   * @param cfg  Contains attribute-value pairs for each configurable
   *             OSPF option attribute 
   * @exception configException  if any of the calls to <code>find</code>
   *                             or <code>findSingle</code> throw such an
   *                             exception.
   */
  public static void config(Configuration cfg) throws configException {
    // this method will only be executed once (that's what's intended, anyway)

    // set any defaults that aren't yet set
    opt = new boolean[num_bool_opts];
    for (int i=0; i<num_bool_opts; i++) {
      opt[i] = false;
    }
    
    if (cfg == null) {
      // if no options were configured we can stop here
      return;
    }

    String str;

    // handle most of the boolean options
    for (int i=0; i<num_bool_opts; i++) {
      str = (String)cfg.findSingle(bool_opt_names[i]);
      if (str != null) {
	opt[i] = Boolean.valueOf(str).booleanValue();
      }
    }

    // determine whether any wrap-up debugging options were selected
    wrapup = false;

    // handle the rest of the boolean options
    str = (String)cfg.findSingle("use_nhi_addressing");
    if (str != null) {
      usenhi = Boolean.valueOf(str).booleanValue();
    }

    // - - - - get the number of this validation test, if it is one - - - -
    str = (String)cfg.findSingle("validation_test");
    if (str != null) {
      validation_test = Integer.parseInt(str);
    }


    // = = = = = = = = check for obsolete config options = = = = = = = = =


  } // end of config

  // --------------------------- Options.debugmsg -------------------------- //
  /**
   * Prints a debugging message.
   *
   * @param ospf    The OSPF instance associated with the message.
   * @param typ     Indicates the type of situation that the message is
   *                reporting on.
   * @param caseno  A case number, applicable to certain message types.
   * @param i       Numeric data whose meaning varies depending on msg type.
   * @param i2      Numeric data whose meaning varies depending on msg type.
   * @param o       An object whose meaning varies depending on msg type.
   * @param o2      An object whose meaning varies depending on msg type.
   */
  public static void debugmsg(sOSPF ospf, int typ, int caseno,
                              int i, int i2, Object o, Object o2) {

    if (!opt[typ]) {
      // if the option to show these messages is not turned on, stop here
      return;
    }

    String str = ospf.debug.hdr();

    switch (typ) {
    case IFS:
      str += "IP interfaces: " + ospf.ip.INTERFACE_COUNT +
             ", OSPF interfaces: " + ospf.ifs.length + "\n";

      if (usenhi) {
        str += "  | typ NHI      ptpNHI   mask st NH     ASNHI  " +
               "area  pASNHI pArea peerNH cost\n";
      } else {
        str += "  | typ IP           ptpIP        mask st NH     AS#    " +
               "area  pAS#   pArea peerNH cost\n";

      }
      for (int j=0; j<ospf.ifs.length; j++) {
        str += "  | " + ospf.ifs[j].toMinString(usenhi) + "\n";
      }
      break;
    case AS_NUM:
      str += "AS#=" + ospf.as_num + "  ASNHI=" + ospf.as_nh + "\n";
      break;
    case AS_AREA:
      str += "AS#=" + ospf.as_num + "  area#s=";
      if (ospf.areas != null) {
        for (int j=0; j<ospf.areas.length; j++) {
          str += ospf.areas[j] + " ";
        }
      }
      str += "\n";
      break;
    case RTR_TYPE:
      str += "router is of type(s): ";
      if (ospf.internal_rtr)    { str += "Internal  "; }
      if (ospf.backbone_rtr)    { str += "Backbone  "; }
      if (ospf.area_border_rtr) { str += "Area Border  "; }
      if (ospf.as_boundary_rtr) { str += "AS Boundary  "; }
      str += "\n";
      break;
    case HELLO:
      str += "\n";
      break;
    case LSA:
      switch (caseno) {
      case 1:
        str += "sent hello packets\n"; // ???
        break;
      case 2:
        str += "ASBR flooding LSA-5\n";
        break;
      }
      break;
    case LSDB:
      switch (caseno) {
      case 1:
        str += "Link State Database\n" + ospf.lsdb.toString("  | ",usenhi);
        break;
      case 2:
        str += "accepting AS external route " + IP_s.IPtoString(i) + "/" + i2 +
               "\n";
        break;
      case 3:
        str += "rcvd LSA-5\n";
        break;
      case 4:
        str += "forwarding LSA-5\n";
        break;
      }
      break;
    case FWD_TABLE:
      switch (caseno) {
      case 1:
        str += "Local Forwarding Table\n" +
             ((RadixTreeRoutingTable)ospf.fwd_table).toString("  | ", usenhi);
        break;
      case 2:
        str += "added " + IP_s.IPtoString(i) + "/" + i2 + ",nxt=" + o +
               " to fwd table\n";
        break;
      }
      break;
    default:
      str += "unrecognized debug message type: " + typ + "\n";
    }

    System.out.print(str);
  }


  // ------------------------ Options.validation_msg ----------------------- //
  /**
   * Prints a message associated with a validation test.
   *
   * @param ospf     The OSPF instance that the message is associated with.
   * @param testnum  The indicative validation test number.
   * @param msgnum   Specifies which message should be printed.
   * @param o        An object whose meaning varies depending on which test
   *                 and which message are specified.
   */
  public static void validation_msg(sOSPF ospf, int testnum, int msgnum,
				    Object o) {

    if (testnum != validation_test) {
      return;
    }

    String str = null;
    String pf = "~#"; // the prefix for all validation output
    String hr = pf + " = = = = = = = = = = = = = = = = = = " +
                "= = = = = = = = = = = = = = = = = = =";
    String successmsg = pf + "             *** TEST SUCCESSFUL ***";
    String failuremsg = pf + "              **** TEST FAILED ****";

    switch (testnum) {
    case ROUTERTYPE1:
      //str = "1";
    case ROUTERTYPE2:
      //if (str == null) {
      //  str = "2";
      //}
      switch (msgnum) {
      case 0: // any introductory test messages
	//System.out.println("\n" + pf + "\n" + hr);
	//System.out.println(pf + " ROUTERTYPE" + str + " Validation Test");
	//System.out.println(hr + "\n" + pf);
	break;
      case 1:
        str = pf + " OSPF router at host " + ospf.nh + " is of type(s): ";
        if (ospf.internal_rtr)    { str += "Internal  "; }
        if (ospf.backbone_rtr)    { str += "Backbone  "; }
        if (ospf.area_border_rtr) { str += "Area Border  "; }
        if (ospf.as_boundary_rtr) { str += "AS Boundary  "; }
        System.out.println(str);
        break;
      }
      break;
    case INTERFACES1:
      //str = "1";
    case INTERFACES2:
      //if (str == null) {
      //  str = "2";
      //}
      switch (msgnum) {
      case 0: // any introductory test messages
	//System.out.println("\n" + pf + "\n" + hr);
	//System.out.println(pf + " INTERFACES" + str + " Validation Test");
	//System.out.println(hr + "\n" + pf);
	break;
      case 1:
        str = pf + " " + ospf.nh + "  OSPF at host " + ospf.nh + " has " +
              ospf.ip.INTERFACE_COUNT + " IP interfaces and " +
              ospf.ifs.length + " OSPF interfaces\n";
        //str += pf + " " + ospf.nh + "  | typ NHI      ptpNHI   mask st " +
        //       "NH     ASNHI  area  pASNHI pArea peerNH cost\n";
        for (int j=0; j<ospf.ifs.length; j++) {
          str += pf + " " + ospf.nh + " " +
                 sOSPF.topnet.ip_to_nhi(IP_s.IPtoString(ospf.ifs[j].ipaddr)) +
                 "  | " + ospf.ifs[j].toMinString(true) + "\n";
        }
        System.out.println(str);
        break;
      }
      break;
    case LSDB1:
      //str = "1";
    case LSDB2:
      //if (str == null) {
      //  str = "2";
      //}
      switch (msgnum) {
      case 0: // any introductory test messages
	//System.out.println("\n" + pf + "\n" + hr);
	//System.out.println(pf + " LSDB" + str + " Validation Test");
	//System.out.println(hr + "\n" + pf);
	break;
      case 1:
        System.out.println(pf + " " + ospf.nh + "   Link State Database for " +
                           "OSPF at host " + ospf.nh);
        String dbstr = ospf.lsdb.toVString(pf+" "+ospf.nh+" ",usenhi);
        System.out.println(dbstr);
//         StringBuffer buf = new StringBuffer(dbstr);
//         int j=0;
//         int k=1;
//         while ((j = dbstr.indexOf("xxx",j)) >= 0) {
//           String numstr = StringManip.pad(""+k,3,'0',false);
//           buf.setCharAt(j,   numstr.charAt(0));
//           buf.setCharAt(j+1, numstr.charAt(1));
//           buf.setCharAt(j+2, numstr.charAt(2));
//           j += 3;
//           k++;
//         }
//         System.out.println(buf);
        break;
      }
      break;
    case FWDTABLE1:
      //str = "1";
    case FWDTABLE2:
      //if (str == null) {
      //  str = "2";
      //}
      switch (msgnum) {
      case 0: // any introductory test messages
	//System.out.println("\n" + pf + "\n" + hr);
	//System.out.println(pf + " FWDTABLE" + str + " Validation Test");
	//System.out.println(hr + "\n" + pf);
	break;
      case 1:
        System.out.println(pf + " " + ospf.nh + ".000 Forwarding Table at " +
                           "router " + ospf.nh);
        String ftstr = ((RadixTreeRoutingTable)ospf.fwd_table).
                                     toString(pf+" "+ospf.nh+".xxx  | ", true);
        StringBuffer buf = new StringBuffer(ftstr);
        int j=0;
        int k=1;
        while ((j = ftstr.indexOf("xxx",j)) >= 0) {
          String numstr = StringManip.pad(""+k,3,'0',false);
          buf.setCharAt(j,   numstr.charAt(0));
          buf.setCharAt(j+1, numstr.charAt(1));
          buf.setCharAt(j+2, numstr.charAt(2));
          j += 3;
          k++;
        }
        System.out.println(buf);
        break;
      }
      break;
    default:
      ospf.debug.err("unknown validation test number: " + testnum);
    }
  }


} // end class Options
